﻿namespace LiteBlog.Components
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using LiteBlog.Common.Entities;
    using LiteBlog.Components.Enums;
    using LiteBlog.Components.Contracts;
    using LiteBlog.Repositories.Contracts;
    using LiteBlog.Common.Components;

    public class TagComp : BaseComp<ITagRepository>, ITagComp
    {
        public TagComp(ITagRepository data, IBlogRepository blogRepo, IPostRepository postRepo, ICacheComp cache, ISiteMapComp sitemapComp) : base(data, cache)
        {
            _blogRepo = blogRepo;
            _postRepo = postRepo;
            _sitemapComp = sitemapComp;
        }

        public ISiteMapComp SiteMapComponent
        {
            get
            {
                return _sitemapComp;
            }
        }

        public string GetNames(string csvIds)
        {
            string[] ids = csvIds.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
            return ids.Select(id => Get(id).Name).Aggregate((cur, next) => cur + ", " + next);
        }

        public IEnumerable<Tag> Get()
        {
            var tags = _cache.Get<List<Tag>>(CacheType.Tags);
            if (tags == null)
            {
                tags = _data.Get().OrderBy(tag => tag.Name).ToList();
                _cache.Put(CacheType.Tags, tags);
            }
            return tags;
        }

        public Tag Get(string id)
        {
            return Get().SingleOrDefault(item => item.TagId.Equals(id));
        }

        public MethodResult Create(Tag item)
        {
            if (IdExists(item.TagId))
                return MethodResult.DuplicateId;
            if (NameExists(item.Name))
                return MethodResult.DuplicateName;
            var methodResult = _data.Create(item) ? MethodResult.CreateSuccess : MethodResult.DuplicateId;

            SiteMapComponent.Add(SiteMapType.Tag, GetSiteMapUrl(item.TagId));
            return methodResult;
        }

        public MethodResult Update(string olid, Tag item)
        {
            var oldItem = Get(olid);
            if (oldItem == null)
                return MethodResult.Failure;

            if (oldItem.TagId != item.TagId)
                if (IdExists(item.TagId))
                    return MethodResult.DuplicateId;

            if (oldItem.Name != item.Name)
                if (NameExists(item.Name))
                    return MethodResult.DuplicateName;

            if (olid != item.TagId)
            {
                ChangeTag(olid, item);
                SiteMapComponent.UpdateUrl(SiteMapType.Tag, GetSiteMapUrl(olid).Location, GetSiteMapUrl(item.TagId).Location);
            }
            else
            {
                SiteMapComponent.Update(SiteMapType.Tag, GetSiteMapUrl(item.TagId));
            }

            _data.Update(olid, item);
            return MethodResult.UpdateSuccess;
        }

        public MethodResult Delete(string id)
        {
            ChangeTag(id, null);
            _data.Delete(id);
            SiteMapComponent.Delete(SiteMapType.Tag, GetSiteMapUrl(id).Location);
            return MethodResult.DeleteSuccess;
        }

        private bool IdExists(string id)
        {
            var item = Get(id);
            return item != null;
        }

        private bool NameExists(string name)
        {
            var item = Get().SingleOrDefault(c => c.Name == name);
            return item != null;
        }

        private SiteMapUrl GetSiteMapUrl(string tagId)
        {
            var siteMapUrl = new SiteMapUrl
            {
                Location = "/Tag/" + tagId,
                LastModifiedTime = DateTime.UtcNow
            };
            return siteMapUrl;
        }

        private void ChangeTag(string oldId, Tag newTag)
        {
            var postInfos = _blogRepo.Get();
            foreach (var postInfo in postInfos)
            {
                bool tagFound = postInfo
                    .Tags
                    .Select(t => t.TagId)
                    .Contains(oldId);

                if (tagFound)
                {
                    var post = _postRepo.Load(postInfo.FileID);
                    var oldTag = post.Tags.Single(t => t.TagId == oldId);
                    post.Tags.Remove(oldTag);
                    if (newTag != null)
                    {
                        post.Tags.Add(newTag);
                    }
                    _postRepo.Save(post);
                }
            }

            _blogRepo.ChangeTag(oldId, newTag.TagId);
        }

        private IBlogRepository _blogRepo;
        private IPostRepository _postRepo;
        private ISiteMapComp _sitemapComp;
    }
}